
package system.communication;

import java.util.HashMap;
import java.util.Set;
/**
 * The Message class defines a Message received from the socket and implements methods
 * to parse through retreive the desired information from a Message.
 * 
 * @author elliot
 */
public class Message
{
    private int id;
    private MessageType type;
    private HashMap<MessageArg, String> arguments;
    
    /**
     * Constructs a Message object based on the string output from the socket.
     * 
     * @param message A String in the form of a message received as output from the
     * socket.
     */
    public Message(String message)
    {
        arguments = new HashMap<MessageArg, String>();
        parse(message);
    }
    
    /**
     * Constructs a Message based on a MessageType enum, and the id prefix of the message
     * note: that LED, SENSOR, and MESSAGE enums from MessageType will always have an id
     * of -1. The Sensor id's are retrieved through the arguments not the id prefix of
     * the message.
     * 
     * @param type a MessageType enum in system.communication.
     * @param id an integer representing the id prefix of the Message.
     */
    public Message(MessageType type, int id)
    {
        this.type = type;
        switch(type)
        {
            case LED:
                this.id = -1;
                break;
            case SENSOR:
                this.id = -1;
                break;
            case MESSAGE:
                this.id = -1;
                break;
            default:
                this.id = id;        
        }
    }

    /**
     * Retreives the message prefix integer id.
     * 
     * @return a integer representing the id for the message prefix specific to retreiving
     * id's of a Block, Switch and HandControl (not for Sensor).
     */
    public int getId()
    {
        return id;
    }
    
    /**
     * Retrieve the Message's Type.
     * 
     * @return a MessageType enum from system.communication.
     */
    public MessageType getType()
    {
        return type;
    }
    
    /**
     * Retrieve an argument from the Message
     * 
     * @param messageArg A MessageArg enum type from system.communication.
     * @return a String representing the value of the argument.
     */
    public String getArg(MessageArg messageArg)
    {
        return arguments.get(messageArg);
    }

    /**
     * Retrieves a set containing all MessageArgs in the Message.
     * @return Set of MessageArg type.
     */
    public Set<MessageArg> getAvailableArgs()
    {
        return arguments.keySet();
    }
    
    /**
     * Sets an argument for use of constructing a Message object.
     * 
     * @param messageArg a MessageArg enum type from system.communication.
     * @param value the String value of the argument.
     */
    public void setArg(MessageArg messageArg, String value)
    {
        if(arguments.containsKey(messageArg))
        {
            arguments.remove(messageArg);
            arguments.put(messageArg, value);
        }
        else
        {
            arguments.put(messageArg, value);
        }
    }
    /**
     * Parses through a message putting MessageArgs into an internal HashMap
     * based on the String parameter.
     *
     * @param message String received by MessageListener from socket.
     */
    private void parse(String message)
    {
        try
        {
            char prefix = message.charAt(1);
            if(prefix == 'B')
            {
                this.type = MessageType.BLOCK;
                this.id = Integer.parseInt(message.substring(message.indexOf("B")+1, message.indexOf("{")));
                arguments.put(MessageArg.BLOCK_DIRECTION, message.split("\\{")[1].split("\\,")[0]);
                arguments.put(MessageArg.BLOCK_SPEED, message.split("\\{")[1].split("\\,")[1].split("\\}")[0]);
            }
            else if(prefix == 'T')
            {
                this.type = MessageType.SWITCH;
                this.id = Integer.parseInt(message.substring(message.indexOf("T")+1, message.indexOf("{")));
                arguments.put(MessageArg.SWITCH_ANGLE, message.split("\\{")[1].split("\\}")[0]);
            }
            else if(prefix == 'U')
            {
                this.type = MessageType.HANDCONTROL;
                this.id = Integer.parseInt(message.substring(message.indexOf("U")+1, message.indexOf("{")));

                if (message.equals("*U1{?}") || message.equals("*U2{?}"))
                {
                    arguments.put(MessageArg.HANDCONTROL_QUERY, "?");
                }
                else
                {
                    arguments.put(MessageArg.HANDCONTROL_HALTED, message.split("\\{")[1].split("\\,")[0]);
                    arguments.put(MessageArg.HANDCONTROL_BLOCK_DIRECTION, message.split("\\{")[1].split("\\,")[1]);
                    arguments.put(MessageArg.HANDCONTROL_CONNECTED, message.split("\\{")[1].split("\\,")[2]);
                    arguments.put(MessageArg.HANDCONTROL_SWITCH_ANGLE, message.split("\\{")[1].split("\\,")[3]);
                    arguments.put(MessageArg.HANDCONTROL_SPEED, message.split("\\{")[1].split("\\,")[4].split("\\}")[0]);
                }
            }
            else if(prefix == 'S')
            {
                this.type = MessageType.SENSOR;
                this.id = -1;
                if(message.indexOf(",")==-1)
                {
                    arguments.put(MessageArg.SENSOR_VALUE_ONE, message.split("\\{")[1].split("\\}")[0]);
                    arguments.put(MessageArg.SENSOR_VALUE_TWO, null);
                }
                else
                {
                    arguments.put(MessageArg.SENSOR_VALUE_ONE, message.split("\\{")[1].split("\\,")[0]);
                    arguments.put(MessageArg.SENSOR_VALUE_TWO, message.split("\\,")[1].split("\\}")[0]);
                }
            }
            else if(prefix == 'M')
            {
                this.type = MessageType.OK;
                this.id = -1;
                arguments.put(MessageArg.MESSAGE_VALUE, message.split("\\{")[1].split("\\}")[0]);
            }
            else
            {
                this.type = MessageType.LED;
                this.id = -1;
                arguments.put(MessageArg.LED_VALUE, message.split("\\{")[1].split("\\}")[0]);
            }
        }
        catch(Exception ex)
        {
            System.out.println("parse: " + ex.getMessage());
            ex.printStackTrace();
            this.type = MessageType.VOID;
            arguments.put(MessageArg.MESSAGE_VALUE, message);
        }
    }
}
